package cn.sylinx.horm.type;

import java.io.InputStream;
import java.io.Reader;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.Month;
import java.time.Year;
import java.time.YearMonth;
import java.time.ZonedDateTime;
import java.util.Date;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.concurrent.ConcurrentHashMap;

import cn.sylinx.horm.dialect.type.TypeHandlerBuilder;
import cn.sylinx.horm.type.handler.BigDecimalTypeHandler;
import cn.sylinx.horm.type.handler.BigIntegerTypeHandler;
import cn.sylinx.horm.type.handler.BlobInputStreamTypeHandler;
import cn.sylinx.horm.type.handler.BooleanTypeHandler;
import cn.sylinx.horm.type.handler.ByteArrayTypeHandler;
import cn.sylinx.horm.type.handler.ByteObjectArrayTypeHandler;
import cn.sylinx.horm.type.handler.ByteTypeHandler;
import cn.sylinx.horm.type.handler.CharacterTypeHandler;
import cn.sylinx.horm.type.handler.ClobReaderTypeHandler;
import cn.sylinx.horm.type.handler.DateTypeHandler;
import cn.sylinx.horm.type.handler.DoubleTypeHandler;
import cn.sylinx.horm.type.handler.EnumOrdinalTypeHandler;
import cn.sylinx.horm.type.handler.EnumTypeHandler;
import cn.sylinx.horm.type.handler.FloatTypeHandler;
import cn.sylinx.horm.type.handler.InstantTypeHandler;
import cn.sylinx.horm.type.handler.IntegerTypeHandler;
import cn.sylinx.horm.type.handler.LocalDateTimeTypeHandler;
import cn.sylinx.horm.type.handler.LocalDateTypeHandler;
import cn.sylinx.horm.type.handler.LocalTimeTypeHandler;
import cn.sylinx.horm.type.handler.LongTypeHandler;
import cn.sylinx.horm.type.handler.MonthTypeHandler;
import cn.sylinx.horm.type.handler.ObjectTypeHandler;
import cn.sylinx.horm.type.handler.ShortTypeHandler;
import cn.sylinx.horm.type.handler.SqlDateTypeHandler;
import cn.sylinx.horm.type.handler.SqlTimeTypeHandler;
import cn.sylinx.horm.type.handler.SqlTimestampTypeHandler;
import cn.sylinx.horm.type.handler.StringTypeHandler;
import cn.sylinx.horm.type.handler.TypeHandler;
import cn.sylinx.horm.type.handler.UnknownTypeHandler;
import cn.sylinx.horm.type.handler.YearMonthTypeHandler;
import cn.sylinx.horm.type.handler.YearTypeHandler;
import cn.sylinx.horm.type.handler.ZonedDateTimeTypeHandler;
import cn.sylinx.horm.type.registrar.TypeHandlerRegistrar;

public class TypeHandlerRegistry implements TypeHandlerBuilder {

    private final Map<Class<?>, TypeHandler<?>> TYPE_HANDLER_MAP = new ConcurrentHashMap<>();
    private final UnknownTypeHandler UNKNOWN_TYPE_HANDLER = new UnknownTypeHandler();
    public static final TypeHandlerRegistry DEFAULT_INSTANCE = new TypeHandlerRegistry();

    protected void registerSystemTypeHandler() {

        register(Boolean.class, new BooleanTypeHandler());
        register(boolean.class, new BooleanTypeHandler());

        register(Byte.class, new ByteTypeHandler());
        register(byte.class, new ByteTypeHandler());

        register(Short.class, new ShortTypeHandler());
        register(short.class, new ShortTypeHandler());

        register(Integer.class, new IntegerTypeHandler());
        register(int.class, new IntegerTypeHandler());

        register(Long.class, new LongTypeHandler());
        register(long.class, new LongTypeHandler());

        register(Float.class, new FloatTypeHandler());
        register(float.class, new FloatTypeHandler());

        register(Double.class, new DoubleTypeHandler());
        register(double.class, new DoubleTypeHandler());

        register(Reader.class, new ClobReaderTypeHandler());
        register(String.class, new StringTypeHandler());

        register(BigInteger.class, new BigIntegerTypeHandler());

        register(BigDecimal.class, new BigDecimalTypeHandler());

        register(InputStream.class, new BlobInputStreamTypeHandler());
        register(Byte[].class, new ByteObjectArrayTypeHandler());
        register(byte[].class, new ByteArrayTypeHandler());

        register(Object.class, new ObjectTypeHandler());

        register(Date.class, new DateTypeHandler());

        register(java.sql.Date.class, new SqlDateTypeHandler());
        register(java.sql.Time.class, new SqlTimeTypeHandler());
        register(java.sql.Timestamp.class, new SqlTimestampTypeHandler());

        register(Instant.class, new InstantTypeHandler());
        register(LocalDateTime.class, new LocalDateTimeTypeHandler());
        register(LocalDate.class, new LocalDateTypeHandler());
        register(LocalTime.class, new LocalTimeTypeHandler());
        register(ZonedDateTime.class, new ZonedDateTimeTypeHandler());
        register(Month.class, new MonthTypeHandler());
        register(Year.class, new YearTypeHandler());
        register(YearMonth.class, new YearMonthTypeHandler());

        register(Character.class, new CharacterTypeHandler());
        register(char.class, new CharacterTypeHandler());

    }

    public TypeHandlerRegistry() {
        // 注册系统类型处理器
        registerSystemTypeHandler();
        // 注册第三方类型处理器
        registerExtTypeHandler();
    }

    protected void registerExtTypeHandler() {
        ServiceLoader<TypeHandlerRegistrar> sl = ServiceLoader.load(TypeHandlerRegistrar.class);
        for (TypeHandlerRegistrar typeHandlerRegistrar : sl) {
            Map<Class<?>, TypeHandler<?>> mappedTypeHandlers = typeHandlerRegistrar.mappedTypeHandlers();
            if (mappedTypeHandlers != null && !mappedTypeHandlers.isEmpty()) {
                mappedTypeHandlers.forEach(this::register);
            }
        }
    }

    @SuppressWarnings({ "unchecked", "rawtypes" })
    public <T> TypeHandler<T> getTypeHandler(Class<T> type) {

        if (type == null) {
            return (TypeHandler<T>) UNKNOWN_TYPE_HANDLER;
        }

        TypeHandler<?> handler = TYPE_HANDLER_MAP.get((Type) type);

        if (handler == null && Enum.class.isAssignableFrom((Class<?>) type)) {

            if (EnumOrdinal.class.isAssignableFrom((Class<?>) type)) {
                // 枚举，使用数值
                handler = new EnumOrdinalTypeHandler((Class<?>) type);
            } else {
                // 枚举，使用名称
                handler = new EnumTypeHandler((Class<?>) type);
            }
        }
        return (TypeHandler<T>) handler;
    }

    protected <T> void register(Class<?> javaType, TypeHandler<? extends T> typeHandler) {
        TYPE_HANDLER_MAP.put(javaType, typeHandler);
    }

    public UnknownTypeHandler getUnknownTypeHandler() {
        return UNKNOWN_TYPE_HANDLER;
    }
}
